#include"EventLoop.h"
#include <fcntl.h>
#include<sys/socket.h>
#include <unistd.h>

using namespace tmms::network;

static thread_local EventLoop*  t_local_eventloop  = nullptr; //每个线程有且仅有一个自己的独立的EventLoop，就是在第一次的时候直接指向它自己了

EventLoop ::EventLoop():epoll_fd_(::epoll_create(1024)),
                     epoll_events_(1024)
{
    if(t_local_eventloop)
    {
        NEWORK_ERROR <<"there is  already a thread of this ";
        exit(-1);
    }
    t_local_eventloop = this; //指向它自己
}

EventLoop::~EventLoop()
{
    Quit();
}

void EventLoop::AddEvent( const EventPtr &event)
{
    auto iter  = events_.find(event->Fd());
    if(iter!=events_.end()) //已经有了，直接return
    {
        return ;
    }
    // if(enable){
    //     event->event_ |= 
    // }
    event->event_ |= KEventRead;
    //添加到map中
    events_[event->Fd()] = event;
 
    //epoll也要加
    struct epoll_event ev;
    memset(&ev,0x00,sizeof(struct epoll_event));
    ev.events = event->event_;
    ev.data.fd=event->fd_;
    epoll_ctl(epoll_fd_,EPOLL_CTL_ADD,event->fd_,&ev);
}


void EventLoop::DelEvent(const EventPtr &event)
{
    auto iter  = events_.find(event->Fd());
    if(iter ==events_.end()) //已经没有了，直接return
    {
        return ;
    }
    events_.erase(iter);
  

    //池子中也要删除
    struct epoll_event ev;
    memset(&ev,0x00,sizeof(struct epoll_event));
    ev.events = event->event_;
    ev.data.fd=event->fd_;
    epoll_ctl(epoll_fd_,EPOLL_CTL_DEL,event->fd_,&ev);
}


bool  EventLoop::EnableEventReading(const EventPtr &event,bool enable)
{
    auto iter  = events_.find(event->Fd());
    if(iter !=events_.end()) //已经没有了，直接return
    {
        //没有增加这个类吗，使能是没有用的
        NEWORK_ERROR<<"cant find event fd :"<< event->Fd();
        return false;
    }
    if(enable)
    {
        event->event_ |= KEventRead;

    }else{
        event->event_ &= ~KEventRead;
    }
    //池子中也要修改
    struct epoll_event ev;
    memset(&ev,0x00,sizeof(struct epoll_event));
    ev.events = event->event_;
    ev.data.fd=event->fd_;
    epoll_ctl(epoll_fd_,EPOLL_CTL_MOD,event->fd_,&ev);//mod是修改的意思
    return true;
}


bool  EventLoop::EnableEventWriting(const EventPtr &event,bool enable)
{
    auto iter  = events_.find(event->Fd());
    if(iter !=events_.end()) //已经没有了，直接return
    {
        //没有增加这个类吗，使能是没有用的
        NEWORK_ERROR<<"cant find event fd :"<< event->Fd();
        return false;
    }
    if(enable)
    {
        event->event_ |= KEventWrite;

    }else{
        event->event_ &= ~KEventWrite;
    }

    //池子中也要修改
    struct epoll_event ev;
    memset(&ev,0x00,sizeof(struct epoll_event));
    ev.events = event->event_;
    ev.data.fd=event->fd_;
    epoll_ctl(epoll_fd_,EPOLL_CTL_MOD,event->fd_,&ev);//mod是修改的意思
    return true;
}


/*ev.data.fd = fd;
将需要监听的 文件描述符（fd） 存储到 epoll_event 结构体的 data 字段中，
以便在事件触发时能够识别具体是哪个文件描述符产生了事件。
将文件描述符添加到 epoll 实例
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        perror("epoll_ctl: add");
        close(fd);
        close(epfd);
        exit(EXIT_FAILURE); }
*/
void EventLoop::Loop()
{
    looping_ = true;
    while (looping_) //监听线程内的epoll
    {
        memset(&epoll_events_[0],0x00,sizeof(struct epoll_event)*epoll_events_.size()); //初始化
        /*
            int nfds = epoll_wait(epfd, events, MAX_EVENTS, 5000);
            也可以这样设计，设置超时时间为5秒
        */

        auto ret = ::epoll_wait(epoll_fd_,
                    (struct epoll_event*)&epoll_events_[0],
                    static_cast<int>(epoll_events_.size()), //max——events
                    -1);   //唯一的阻塞点，唤醒条件：直到至少有一个被监控的文件描述符上发生事件
            std::cout<<"这里是阻塞点"<<std::endl;
        if(ret >= 0 ){ //有事件就绪的时候//返回值 ret == 0：超时无事件​
            for(int i =0 ; i<ret;i++){
                struct epoll_event &ev = epoll_events_[i];
                if(ev.data.fd<=0) {
                    continue;
                }
                
                auto iter = events_.find(ev.data.fd);
                if(iter == events_.end())
                {
                    continue;
                }
                EventPtr &event = iter->second;

                if(ev.events&EPOLLERR)
                {
                    int error = 0 ;
                    socklen_t len = sizeof(error);
                    getsockopt(event->Fd(),SOL_SOCKET,SO_ERROR,&error,&len);
                    event->OnError(strerror(error));
                }else if((ev.events&EPOLLHUP)&&!(ev.events&EPOLLIN))
                {
                   event->OnClose(); //执行关闭事件
                }else if(ev.events & (EPOLLIN | EPOLLPRI))
                {
                    event->OnRead(); // 执行读事件
                }else if(ev.events & EPOLLOUT)
                {
                    event->OnWrite(); //执行写事件
                }
            }
            if(ret  == epoll_events_.size())
            {
                epoll_events_.resize(epoll_events_.size()*2);
            } 
            RunFuctions(); //执行任务队列
        }else if(ret < 0)
        { 
            NEWORK_ERROR<< "epoll wait error.error:"<<errno;
        }
    }
}

void EventLoop::Quit()
{
    looping_=false;
}



void EventLoop::WakeUp() //测试用的管道事件
{
    if(!pipe_event_)
    {
        std::cout<<"wode tian "<<std::endl;
        pipe_event_ = std::make_shared<PipeEvent>(this);
        AddEvent(pipe_event_);
    }

    //需要唤醒的时候就写数据就行
    int64_t tmp =1;
    pipe_event_->Wirte((const char*)&tmp,sizeof(tmp));

    std::cout<<pipe_event_->a<<std::endl;

}

void EventLoop::AssertInLoopThread()
{
    if(!IsInLoopThread()){ //判断是否在同一个线程？？？ 
        NEWORK_ERROR<<"It is forbidden to run loop on other thread!!!@ ";
        exit(-1);
    }
}
bool EventLoop::IsInLoopThread() const
{ //通过这个与自己的this指针来看，他们是否在同一个线程下
    return t_local_eventloop == this;
}

void EventLoop::RunInLoop(const Func& f) // 左值引用入队
{ // 任务入队

  if(IsInLoopThread())
    {
        f();
    }else
    {
        std::lock_guard<std::mutex> lk(lock_);
        functions_.push(f);

        WakeUp();
    }  
}

void EventLoop::RunInLoop( Func&& f) //右值引用（可变的）入队
{ // 任务入队
    if(IsInLoopThread())
    {
        f();
    }else
    {
        std::lock_guard<std::mutex> lk(lock_);
        functions_.push(std::move(f));

      
    }  
      WakeUp();
}


void EventLoop::RunFuctions()
{
    std::lock_guard<std::mutex> lk(lock_);
    while(!functions_.empty())
    {
        auto &f = functions_.front();
        f();
        functions_.pop();
    }
}

